/*appdaemon.c*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include <dirent.h>
#include <string.h>
#include <errno.h>
#include "appmanager.h"

/*
 * 通过判断/proc/xxx(pid) 是否存在来判断app 是否运行
 * */
static int _app_mgr_is_process_running(int pid) 
{
#if 1
	char proc_str[128];
	snprintf(proc_str, sizeof(proc_str), "/proc/%d", pid);
	
	/*
    if(0 == access(proc_str, F_OK))
    {
    	printf("*** process is running!!\n");
    	return 1;
    }    
	else
    {
    	printf("*** process is missing!!\n");
    	return 0;
    }    
	*/

	DIR *proc_dir = opendir(proc_str);
	if (proc_dir) {
    	//printf("**** process is running %d!!\n", __LINE__);
    	closedir(proc_dir);
    	return 1;
	} else {
    	//printf("**** process is missing %d!!\n", __LINE__);
    	return 0;
	}    
#else
    if (kill(pid, 0) == 0) 
	{
        // the process is running
        return 1;
    } else if (errno == ESRCH) {
        // not found process
        return 0;
    } else {
        // fail
        perror("kill");
        return 0;
    }
#endif	
}

/*
 * app mgr , app 运行中是while(1) -> sleep , app 没有
 * 运行，重新启动线程运行 
 * */
static int _app_mgr_daemon_loop(const char *daemon_app)
{
	int app_pid = -1;
	app_pid = app_mgr_pid_get(daemon_app);

	if (app_pid > 0)
	{
		//found pid.
		
		//pid = (pid_t)app_pid;
		printf("%s(), loop daemon check: %s!\n", __func__, daemon_app);
		while (1)
		{
			if (0 == _app_mgr_is_process_running(app_pid))
			{
				printf("%s(), loop daemon check: restart!!!\n", __func__);
				app_mgr_daemon_start(daemon_app);
				return 0;
			}
			sleep(1);
		}
	}

	return -1;
}

static void *_app_mgr_daemon_task(void *arg)
{
	pid_t pid;
	int restart_app = 1;
	int exit_signal;
	
	char *name = (char*)arg;
	printf("%s(), daemon name:%s\n", __func__, name);


	//check if the application is running already.
	if (0 == _app_mgr_daemon_loop(name))
	{
		//loop checking..., restart application...
		return NULL;
	}
	else
	{
        /* 
         * fork() create child process ,and child process run form here
         * pid == 0, but parent process get pid != 0  ,use return pid
         * to differ child proc and parent proc
         * */
		pid = fork(); 
	}

    if (pid == 0) 
    {
		printf("Child process (PID: %d) is doing a task.\n", getpid());
		
        /*
         * 加载app 到子进程的内存中运行，如果有程序则会自行替换 
         * */
        execlp(name, name, "", NULL); 
        exit(0); 
    } 
    else if (pid > 0) 
    {
		printf("child pid:%d\n", pid);
		printf("Parent process (PID: %d) continues with other tasks.\n", getpid());
		
        int status;
        /*
         * wait /waitpid 用于阻塞/ 非阻塞等待子进程(进程)终止并回收
         * 进程资源
         * */
        pid_t wpid = waitpid(pid, &status, 0); 
		//waitid(P_PID, pid, (siginfo_t *)&status, WEXITED);

		printf("wait child pid:%d\n", wpid);
        if (wpid == -1) 
        {
            perror("waitpid failed");
            //exit(EXIT_FAILURE);
        }

		//Note: the daemon app(hcprojector) must exit(signal_no), so that the signal_no
		//can be passed to here.
        /* 获取子进程终止时的不同状态并处理*/
        if (WIFEXITED(status)) 
        {
            // Child process normal exit
			exit_signal = WEXITSTATUS(status);
            printf("Child process normal exit with status %d\n", exit_signal);
			if (15 == exit_signal)
			{
				//signal 15: killall signal, kill pid; 
				restart_app = 0;
			}
			else if (2 == exit_signal)
			{
				//signal 2: Ctrl+c. We do not restart app,
				restart_app = 0;
			}

        } 
        else if (WIFSIGNALED(status)) 
        {
            // Child process terminated, kill -9 pid
			exit_signal = WTERMSIG(status);
            printf("Child process terminated by signal %d\n", exit_signal);
			if (9 == exit_signal)
			{//kill -9
				restart_app = 0;
			}
        }
        else if (WIFSTOPPED(status)) 
        {
            // Child process stopped.
			exit_signal = WSTOPSIG(status);
            printf("Child process stopped by signal %d\n", exit_signal);
        }
    
		if (restart_app)
		{
			printf("re-start app: %s\n", name);
			app_mgr_daemon_start(name);
		}
    } 
    else 
    {
        perror("fork failed");
        exit(EXIT_FAILURE);
    }
	return NULL;
}

//check if the application if exist
// 通过access check file whether exist
static int _app_mgr_app_is_exist(const char *daemon_app)
{
	char app_path[256];
	int is_exist = 0;

    if(0 == access(daemon_app, F_OK))
    	return 1;

   	if (*daemon_app != '.')
	{
	    snprintf(app_path, sizeof(app_path), "/usr/bin/%s", daemon_app);
	    if(0 == access(app_path, F_OK))
	    	return 1;

	    snprintf(app_path, sizeof(app_path), "/bin/%s", daemon_app);
	    if(0 == access(app_path, F_OK))
	    	return 1;
	}

	return 0;
}

int app_mgr_app_is_runing(const char *app_name)
{
	int app_pid = -1;
	app_pid = app_mgr_pid_get(app_name);

	if (app_pid > 0)
	{
		//found pid.
		return _app_mgr_is_process_running(app_pid);
	}
	return 0;
}

/*
 * 通过遍历/proc/xxx(pid)/cmdline 是否存在来
 * 取得具体的app pid 
 * */
int app_mgr_pid_get(const char *app_name) 
{
    DIR *d;
    struct dirent *dir;
    char filename[256];
	char cmdline[256];
	char *process_name;
	int pid = -1;

	process_name = (char*)app_name;
	//skip ".", "/" of string lefg
	while('.' == *process_name || '/' == *process_name)
		process_name ++;
	
    d = opendir("/proc");
    if (d) 
    {
        while ((dir = readdir(d)) != NULL) 
        {
            if (dir->d_type == DT_DIR) { 
                snprintf(filename, sizeof(filename), "/proc/%s/cmdline", dir->d_name);
                FILE *fp = fopen(filename, "r");
                if (fp) 
                {
                    if (fgets(cmdline, sizeof(cmdline), fp) != NULL) 
                    {
                        if (strstr(cmdline, process_name) != NULL) 
                        {
							pid = atoi(dir->d_name);
                            printf("Found PID: %d\n", pid);
							break;
                        }
                    }
                    fclose(fp);
                }
            }
        }
        closedir(d);
    }
    return pid;
}

void app_mgr_daemon_start(const char *daemon_app)
{
	if (app_mgr_is_upgrading())
		return;

	printf("%s(), daemon name:%s\n", __func__, daemon_app);

	if (0 == _app_mgr_app_is_exist(daemon_app))
	{
		printf("%s(), daemon name:%s is not exist!\n", __func__, daemon_app);
		return;
	}

    pthread_t thread_id = 0;
    pthread_attr_t attr;
    //create the message task
    pthread_attr_init(&attr);
    pthread_attr_setstacksize(&attr, 0x2000);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED); //release task resource itself
    pthread_create(&thread_id, &attr, _app_mgr_daemon_task, (void*)daemon_app);
    pthread_attr_destroy(&attr);
}


